package com.evil.kingdom.service.person.general.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.evil.common.core.util.StreamUtil;
import com.evil.kingdom.pojo.dto.person.base.PersonDto;
import com.evil.kingdom.pojo.dto.person.general.GeneralDto;
import com.evil.kingdom.pojo.dto.person.soldier.SoldierDto;
import com.evil.kingdom.pojo.entity.person.base.Person;
import com.evil.kingdom.pojo.entity.person.general.General;
import com.evil.kingdom.pojo.entity.tactics.Tactics;
import com.evil.kingdom.pojo.enums.PersonTypeEnum;
import com.evil.common.mongo.service.impl.SingleServiceImpl;
import com.evil.kingdom.repository.person.general.GeneralRepository;
import com.evil.kingdom.service.person.base.PersonService;
import com.evil.kingdom.service.person.general.GeneralService;
import com.evil.kingdom.service.person.soldier.SoldierService;
import com.evil.kingdom.service.tactics.TacticsService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;

/**
 * 武将
 *
 * @author liyang
 * @date 2024-05-22 16:57
 */
@Slf4j
@Service
@AllArgsConstructor
public class GeneralServiceImpl extends SingleServiceImpl<GeneralRepository, General> implements GeneralService {

    private final PersonService personService;
    private final TacticsService tacticsService;
    private final SoldierService soldierService;

    @Override
    public PersonTypeEnum type() {
        return PersonTypeEnum.GENERAL;
    }

    @Override
    public Optional<GeneralDto> isExistDtoByName(String name) {
        return personService.isExistByName(name).flatMap(this::isExistDtoByPerson);
    }

    @Override
    public GeneralDto findDtoByName(String name) {
        return this.findDtoByPerson(personService.findByName(name));
    }

    @Override
    public List<GeneralDto> isExistDtoListByNames(Collection<String> names) {
        if (CollectionUtil.isEmpty(names)) {
            return new ArrayList<>();
        }
        List<PersonDto> persons = personService.findDtoByNames(names);
        Map<String, PersonDto> personMap = StreamUtil.toMapK(persons, PersonDto::getName);

        List<General> generals = this.isExistByNames(personMap.keySet());
        return this.toDto(personMap, generals, tacticsService::isExistByNames, soldierService::isExistDtoListByNames);
    }

    @Override
    public List<GeneralDto> findDtoListByNames(Collection<String> names) {
        if (CollectionUtil.isEmpty(names)) {
            return new ArrayList<>();
        }
        List<PersonDto> persons = personService.findDtoByNames(names);
        Map<String, PersonDto> personMap = StreamUtil.toMapK(persons, PersonDto::getName);

        List<General> generals = this.findByNames(personMap.keySet());
        return this.toDto(personMap, generals, tacticsService::isExistByNames, soldierService::findDtoListByNames);
    }

    @Override
    public Optional<GeneralDto> isExistDtoByPerson(Person person) {
        return this.isExistDtoByPersonDto(personService.findDtoByPerson(person));
    }

    @Override
    public GeneralDto findDtoByPerson(Person person) {
        return this.findDtoByPersonDto(personService.findDtoByPerson(person));
    }

    @Override
    public Optional<GeneralDto> isExistDtoByPersonDto(PersonDto personDto) {
        return this.isExistByName(personDto.getName()).map(e -> {
            List<Tactics> tactics = tacticsService.isExistByNames(e.getTacticsNames());
            List<SoldierDto> soldierSet = soldierService.isExistDtoListByNames(e.getSoldiers());
            return this.toDto(personDto, e, new HashSet<>(tactics), new HashSet<>(soldierSet));
        });
    }

    @Override
    public GeneralDto findDtoByPersonDto(PersonDto personDto) {
        General general = this.findByName(personDto.getName());
        List<Tactics> tactics = tacticsService.isExistByNames(general.getTacticsNames());
        List<SoldierDto> soldierSet = soldierService.isExistDtoListByNames(general.getSoldiers());
        return this.toDto(personDto, general, new HashSet<>(tactics), new HashSet<>(soldierSet));
    }

    private List<GeneralDto> toDto(Map<String, PersonDto> personMap, Collection<General> generals, Function<Collection<String>, Collection<Tactics>> queryTactics, Function<Collection<String>, Collection<SoldierDto>> querySoldier) {
        // 武将 战术mao
        Map<String, Set<String>> generalTacticsMap = new HashMap<>();
        // 武将 士兵map
        Map<String, Set<String>> generalSoldiersMap = new HashMap<>();
        // 所有士兵
        HashSet<String> tacticsNames = new HashSet<>();
        HashSet<String> soldierNames = new HashSet<>();
        generals.forEach(e -> {
            generalTacticsMap.put(e.getName(), e.getTacticsNames());
            tacticsNames.addAll(e.getTacticsNames());
            generalSoldiersMap.put(e.getName(), e.getSoldiers());
            soldierNames.addAll(e.getSoldiers());
        });
        // 获取士兵
        Collection<SoldierDto> soldiers = querySoldier.apply(soldierNames);
        Map<String, SoldierDto> soldierMap = StreamUtil.toMapK(soldiers, SoldierDto::getName);
        // 获取战术
        Collection<Tactics> tactics = queryTactics.apply(tacticsNames);
        Map<String, Tactics> tacticsMap = StreamUtil.toMapK(tactics, Tactics::getName);
        // 返回
        return StreamUtil.transListT(generals, general -> {
            // 当前武将战术
            Set<Tactics> tacticsSet = StreamUtil.transSetFT(generalTacticsMap.get(general.getName()), tacticsMap::containsKey, tacticsMap::get);
            // 当前武将士兵
            Set<SoldierDto> soldierSet = StreamUtil.transSetFT(generalSoldiersMap.get(general.getName()), soldierMap::containsKey, soldierMap::get);
            return this.toDto(personMap.get(general.getName()), general, tacticsSet, soldierSet);
        });
    }

    private GeneralDto toDto(PersonDto person, General general, Set<Tactics> tacticsSet, Set<SoldierDto> soldierSet) {
        GeneralDto generalDto = BeanUtil.copyProperties(person, GeneralDto.class);
        BeanUtil.copyProperties(general, generalDto);
        generalDto.setTactics(tacticsSet);
        generalDto.setSoldierSet(soldierSet);
        return generalDto;
    }
}
